<?php

namespace app\common\logic;

class JssdkLogic
{
 
    private $appId;
    private $appSecret;
    public  $wxConfig = [];

    public function __construct() {
        $this->wxConfig = tpCache('weapp');
        $this->appId    = $this->wxConfig['weapp_appid'];
        $this->appSecret= $this->wxConfig['weapp_secrete'];
    }

    //jssdk分享参数
    public function shareParam($url = '')
    {
        $param['appId'] = $this->appId;                     //公众号的唯一标识
        $param['nonceStr'] = $this->createNonceStr();       //随机字符串
        $param['timestamp'] = time();                       //时间戳
        $ticket = $this->getJsApiTicket();
        if(empty($url)){
            $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
            $url =$protocol.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
        }

        $str1 = 'jsapi_ticket=' . $ticket . '&noncestr=' . $param['nonceStr'] . '&timestamp=' . $param['timestamp'] . '&url=' . $url;
        $param['signature'] = sha1($str1);                   //签名
        return $param;
    }

    /**
     * 获取accessToken
     * @return type
     */
    public function getAccessToken()
    {
        $accessToken = \think\Cache::get('JssdkAccessToken');
        if(!$accessToken){
            $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appId}&secret={$this->appSecret}";
            $return = https_request($url);
            if(empty($return))
                return false;
            $accessToken = json_decode($return,1)['access_token'];
            \think\Cache::set('JssdkAccessToken', $accessToken, 7000);
        }
        return $accessToken;
    }

    /**
     * 根据 access_token 获取 ticket
     * @return type
     */
    public function getJsApiTicket()
    {
        $JsApiTicket = \think\Cache::get('JssdkJsApiTicket');
        if(!$JsApiTicket){
            $access_token = $this->getAccessToken();
            $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={$access_token}&type=jsapi";
            $return = https_request($url);
            $JsApiTicket = json_decode($return,1)['ticket'];
            \think\Cache::set('JssdkJsApiTicket', $JsApiTicket, 7000);
        }

        return $JsApiTicket;
    }

    // 随机字符串
    public function createNonceStr($length = 16)
    {
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }


    /**
     * 统一下单
     *
     * @param  array $order 订单 必须包含支付所需要的参数 body(产品描述)、total_fee(订单金额)、 out_trade_no(订单号)、product_id(产品id)、 trade_type(类型：JSAPI，NATIVE，APP)
     */
    public function unifiedOrder($order,$openId,$notify_url = ''){
        // 获取配置项
        $weixinpay_config = $this->wxConfig;
        $weixinpay_config['NONCE_STR'] = $this->createNonceStr(); //随机字符串
        $config = array(
            'appid'            => $weixinpay_config['weapp_appid'],
            'mch_id'           => $weixinpay_config['mch_id'],
            'nonce_str'        => $weixinpay_config['NONCE_STR'],
            'sign_type'        => 'MD5',
            'body'             => '希尔顿牙刷',
            'out_trade_no'     => $order['order_sn'],
            'total_fee'        => $order['total_amount']*100,
            // 'total_fee'        => 1,
            'notify_url'       => $notify_url ? $notify_url : $weixinpay_config['NOTIFY_URL'],
            'spbill_create_ip' => request()->ip(),
            'trade_type'       => 'JSAPI',
            'openid'           => $openId,
        );

        $data = $config;
        // 生成签名
        $sign = $this->makeSign($data);
        $data['sign'] = $sign;
        $xml = $this->toXml($data);
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';//接收xml数据的文件
        $header[] = "Content-type: text/xml";//定义content-type为xml,注意是数组
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //兼容本地没有指定curl.cainfo路径的错误
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        $response = curl_exec($ch);
        if (curl_errno($ch)) {
            // 显示报错信息；终止继续执行
            return curl_error($ch);
        }
        curl_close($ch);
        $result = $this->toArray($response);
        // 显示错误信息
        //if ($result['return_code'] == 'FAIL') {
        //    return $result['return_msg'];
        //}
        $result['nonce_str'] = $weixinpay_config['NONCE_STR'];
        $result['out_trade_no'] = $config['out_trade_no'];  //流水号
        return $result;
    }


    /**
     * 生成签名
     * @return 签名
     */
    public function makeSign($data){
        // 去空
        $data = array_filter($data);
        //签名步骤一：按字典序排序参数
        ksort($data);
        $string_a = http_build_query($data);
        $string_a = urldecode($string_a);
        //签名步骤二：在string后加入KEY
        $config = $this->wxConfig;
        $string_sign_temp = $string_a . "&key=" . $config['KEY'];
        //签名步骤三：MD5加密
        $sign = md5($string_sign_temp);
        // 签名步骤四：所有字符转为大写
        $result = strtoupper($sign);

        return $result;
    }



    /**
     * 输出xml字符
     * @throws WxPayException
     **/
    public function toXml($data){
        if (!is_array($data) || count($data) <= 0) {
            throw new WxPayException("数组数据异常！");
        }
        $xml = "<xml>";
        foreach ($data as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";

        return $xml;
    }

     /**
     * 将xml转为array
     * @param  string $xml xml字符串
     * @return array       转换得到的数组
     */
    public function toArray($xml){
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $result = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);

        return $result;
    }

}